package polyhedron;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * test
 * @author normenhansen
 */
public class Polyhedron extends SimpleApplication implements ActionListener, AnalogListener
{
    
    @Override
    public void destroy()
    {
        super.destroy();
        
        saveSettings();
    }
    public static Polyhedron INSTANCE = new Polyhedron();
    
    protected Polyhedron()
    {
        
    }
    
    public static void main(String[] args)
    {
        Polyhedron.INSTANCE.start();
    }

    void saveSettings()
    {
        File file = new File("polyhedron.config");
        try
        {
            FileOutputStream fs = new FileOutputStream(file);
            settings.save(fs);
            fs.close();
        }
        catch(IOException ex)
        {
            
        }
    }
    
    boolean loadSettings()
    {
        File file = new File("polyhedron.config");
        if(file.exists())
        {
            try
            {
                FileInputStream fs = new FileInputStream(file);
                settings.load(fs);
                fs.close();
                return true;
            }
            catch(IOException ex) 
            {
                
            }
        }
        return false;
    }
    
    private void initializeSettings()
    {
        if(!loadSettings())
        {
            settings.setTitle("Polyhedron");
            settings.putInteger("Version", 1);
        }
    }
    
    //Player stuff
    private Node playerNode = new Node("Player.Local");
    private Material playerMaterial;
    private ColorRGBA playerColor = new ColorRGBA(0, .5f, .5f, 0);
    private BetterCharacterControl playerPhyics;
    private Vector3f playerVelocity = Vector3f.ZERO;
    private boolean playerCrouched = false;
    
    //Floor stuff
    private Node floorNode;
    private Material floorMaterial;
    private ColorRGBA floorColor = ColorRGBA.LightGray;
    private RigidBodyControl floorPhyicsControl;
    
    //Wall stuff
    private Node wallNode = new Node("Walls");
    private Material wallMaterial;
    private ColorRGBA wallColor = ColorRGBA.LightGray;
    private RigidBodyControl wallPhyicsControl;
    
    //Physics stuff
    private BulletAppState bulletAppState;
    private Vector3f gravity = new Vector3f(0.0f, -9.81f, 0.0f);
    
    //Input stuff
    private ActionListener actionListener = this;
    private AnalogListener analogListener = this;
    
    private void crouchPlayer()
    {
        playerNode.getChild("Player.Local.Stand").setCullHint(Spatial.CullHint.Always);
        playerNode.getChild("Player.Local.Crouch").setCullHint(Spatial.CullHint.Inherit);
    }
    
    private void standPlayer()
    {
        playerNode.getChild("Player.Local.Crouch").setCullHint(Spatial.CullHint.Always);
        playerNode.getChild("Player.Local.Stand").setCullHint(Spatial.CullHint.Inherit);
    }
    
    private void initializePlayer()
    {
        //setup player physics
        playerPhyics = new BetterCharacterControl(0.5f, 2.0f, 90.71f);
        System.out.println(playerPhyics.getJumpForce());
        bulletAppState.getPhysicsSpace().add(playerPhyics);
        
        //setup player material
        playerMaterial = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        playerMaterial.setBoolean("UseMaterialColors", true);
        playerMaterial.setColor("Diffuse", playerColor );
        playerMaterial.setColor("Ambient", playerColor );
        
        //setup player model
        Node tmp = PlayerNode.construct("Player.Local.Stand", playerMaterial, 2.0f);
        tmp.setLocalTranslation(0, 1.0f, 0);
        playerNode.attachChild(tmp);
        tmp = PlayerNode.construct("Player.Local.Crouch", playerMaterial, 1.0f);
        tmp.setLocalTranslation(0, 0.5f, 0);
        playerNode.attachChild(tmp);
        standPlayer();
        rootNode.attachChild(playerNode);
        playerNode.addControl(playerPhyics);
        playerPhyics.warp(new Vector3f(0, 3, 0));
    }
    
    private void initializeFloorMaterial()
    {
        if(floorMaterial == null)
        {
            floorMaterial = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
            floorMaterial.setBoolean("UseMaterialColors", true);
            floorMaterial.setColor("Diffuse", floorColor );
            floorMaterial.setColor("Ambient", floorColor );            
        }
    }
    
    private void initializeFloor()
    {
        initializeFloorMaterial();
        
        floorNode = PlayerNode.constructFloor("Floor.Standard", floorMaterial);
        rootNode.attachChild(floorNode);
        
        floorPhyicsControl = new RigidBodyControl(0.0f);
        floorNode.addControl(floorPhyicsControl);
        bulletAppState.getPhysicsSpace().add(floorPhyicsControl);
    }
    
    private void initializeWalls()
    {
        initializeFloorMaterial();
        wallMaterial = floorMaterial;
        
        Node tmp = PlayerNode.constructWall("Wall.Left", floorMaterial);
        tmp.setLocalTranslation(-9.5f, 4.5f, 0.4f);
        wallNode.attachChild(tmp);
        tmp = PlayerNode.constructWall("Wall.Right", floorMaterial);
        tmp.setLocalTranslation(9.5f, 4.5f, 0.4f);
        wallNode.attachChild(tmp);
        
        wallNode.attachChild(initializeObstacles());
        
        rootNode.attachChild(wallNode);
        
        wallPhyicsControl = new RigidBodyControl(0.0f);
        wallNode.addControl(wallPhyicsControl);
        bulletAppState.getPhysicsSpace().add(wallPhyicsControl);
    }
    
    private Node initializeObstacles()
    {
        initializeFloorMaterial();
        Node obstacles = new Node("Block.Default");
        Node tmp = PlayerNode.constructObstacle("Block.01", wallMaterial);
        tmp.setLocalTranslation(-5.5f, 2.1f, 0.4f);
        obstacles.attachChild(tmp);
        tmp = PlayerNode.constructObstacle("Block.02", wallMaterial);
        tmp.setLocalTranslation(5.5f, 1.0f, 0.4f);
        obstacles.attachChild(tmp);
        tmp = PlayerNode.constructObstacle("Block.03", wallMaterial);
        tmp.setLocalTranslation(6.5f, 1.0f, 0.4f);
        obstacles.attachChild(tmp);
        tmp = PlayerNode.constructObstacle("Block.04", wallMaterial);
        tmp.setLocalTranslation(7.5f, 2.0f, 0.4f);
        obstacles.attachChild(tmp);
        tmp = PlayerNode.constructObstacle("Block.05", wallMaterial);
        tmp.setLocalTranslation(8.5f, 2.0f, 0.4f);
        obstacles.attachChild(tmp);
        return obstacles;
    }
    
    private void initializeLighting()
    {
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection(new Vector3f(-1,-1,-1).normalizeLocal());
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun);        
    }
    
    private void initializeStates()
    {
        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
        bulletAppState.getPhysicsSpace().setGravity(gravity);
        //bulletAppState.setDebugEnabled(true);
    }
    
    private void initializeKeys()
    {
        inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addMapping("Crouch", new KeyTrigger(KeyInput.KEY_LSHIFT));
        inputManager.addListener(this.actionListener, "Jump", "Crouch");
        
        inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addListener(this.analogListener, "Right", "Left");
    }
    
    @Override
    public void simpleInitApp()
    {
        //stateManager.detach(stateManager.getState(FlyCamAppState.class));
        flyCam.setEnabled(false);
        //inputManager.setCursorVisible(true);
        
        initializeSettings();
        initializeStates();
        initializeLighting();
        initializePlayer();
        initializeFloor();
        initializeWalls();
        initializeKeys();
        
        cam.setLocation(new Vector3f(0, 10, 20));
        cam.lookAt(new Vector3f(0, 5, 0), cam.getUp());
        viewPort.setBackgroundColor(ColorRGBA.DarkGray);
        
        speed = 3;
        
        //Utilities.setAssetManager(this.assetManager);
        //ode tmp = OrientationNode.construct("Orientation");
        //rootNode.attachChild(tmp);
        
        //CameraAttachmentState myState = new CameraAttachmentState();
        //myState.attach(tmp);
        //this.stateManager.attach(myState);

    }
    
    public void onAction(String name, boolean isPressed, float tpf)
    {
        
        if(name.equals("Jump"))
        {
            if(isPressed)
            {
                playerPhyics.jump();
            }
        }
        else if(name.equals("Crouch"))
        {
            playerPhyics.setDucked(isPressed);
        }
        else if(name.equals("Color"))
        {
            playerColor = ColorRGBA.randomColor();
            playerMaterial.setColor("Diffuse", playerColor );
            playerMaterial.setColor("Ambient", playerColor );
            System.out.println(tpf);
            System.out.println();
        }
    }
    public void onAnalog(String name, float value, float tpf)
    {
        if(name.equals("Right"))
        {
            playerVelocity.x = speed;
        }
        else if(name.equals("Left"))
        {
            playerVelocity.x = -speed;
        }
    }
    
    @Override
    public void simpleUpdate(float tpf)
    {
        playerPhyics.setWalkDirection(playerVelocity);
        playerVelocity.x = 0;
        if(playerPhyics.isDucked())
        {
            if(!playerCrouched)
            {
                playerCrouched = true;
                crouchPlayer();
            }
        }
        else
        {
            if(playerCrouched)
            {
                playerCrouched = false;
                standPlayer();
            }            
        }
        Vector3f location = playerNode.localToWorld(playerNode.getLocalTranslation(), null);
        cam.setLocation(cam.getLocation().setX(location.x/2));
    }

    @Override
    public void simpleRender(RenderManager rm)
    {
        //TODO: add render code
    }
}
